#ifndef __qyeditorbase__
#define __qyeditorbase__

#include <QList>
#include <QMap>
#include <QWidget>
#include <QVariant>
#include <QySciEdit>

class QLabel;
class QyTabPagePrivate;
class QyTabWidget;
class QyFindDialog;
class QyReplaceDialog;
class QyGotoDialog;
class QyCodecDialog;


/*! \class QyEditorBase
	\brief Класс обеспечивающий весь базовый функционал текстового редактора.

	QyEditorBase - это виджет - текстовый редактор, позволяющий открывать для редактирования множество файлов. Файлы можно группировать, группа файлов образует набор закладок, каждый набор закладок имеет уникальное имя. Это обеспечивает виджет QyTabWidget. При открытии файла, пользователь может указать к какой группе он принадлежит, если пользователь не указал группу, то файл открывается в текущей группе, если групп не существует, то создается новая, с именем по умолчанию default.

	При модификации файла (открытого в редакторе) QyEditorBase, формирует "строку состояния" в которую могут входить ряд характеристик относящихся к процессу редактированияя и испускает сигнал statusChanged. Пользователь с помощью метода setStatusPattern может задать шаблон для формирования строки. Сама строка представляет собой текст, который содержит специальные "ключи", которые заменяются на соответствующае этим ключам значения.

	Список ключей:
	* ${FILENAME}         - имя файла (только имя)
	* ${FILEPATH}         - абсолютное имя файла
	* ${TOTALLINES}       - количество строк
	* ${LINE}             - текущая строка (на которой стоит курсор)
	* ${COL}              - текущий символ в строке
	* ${LINELENGTH}       - длинна текущей строки
	* ${SELLINESCOUNT}    - кол-во выделенных строк
	* ${SELSYMCOUNT}      - кол-во выделенных символов
	* ${LEXERNAME}        - имя текущего лексера
	* ${FILESIZE}         - размер файла (показывает реальный размер файла, т.е. размер сохраненных данных)
	* ${ENCODING}         - кодировка, если кодировка не была указана в первых 2х строках файла (encoding <codecname>), то используется кодировка системы или та, которая была указана при открытии файла

	QyEditorBase позволяет сохранять/загружать сессии в/из файл(а). Файл сесси имеет формат ini - файла и содержит: список групп файлов, файлы, номер строки на которой находилась каретка (для каждого файла), список закладок (для каждого файла).

	При сохранении файла, QyEditorBase испускает сигнал fileSaved.

	QyEditorBase имеет диалоги "поиск", "поиск и замена".

	Так же, в QyEditorBase интегрирован конфигуратор для настройки редакторов. Конфигурация осуществляется в два этапа:
	1. Установить список свойств при помощи статического метода setSciEditConfig. Метод в качестве аргумента принимает QMap<QString,QVariant> содержащий список свойств и их значений (Список свойст ниже).
	2. Применить свойства для конкретного объекта QyEditorBase, используя метод updateConfig().

	Свойства:
	markers.arrow.back                    - цвет фона для маркера "указатель" (пока не используется)
	markers.arrow.fore                    - цвет контура для маркера "указатель" (пока не используется)
	markers.error.back                    - цвет фона для маркера "ошибка" (пока не используется)
	markers.error.fore                    - цвет контура для маркера "ошибка" (пока не используется)
	markers.warning.back                  - цвет фона для маркера "предупреждение" (пока не используется)
	markers.warning.fore                  - цвет контура для маркера "предупреждение" (пока не используется)
	markers.bookmark.back                 - цвет фона для маркера "закладка"
	markers.bookmark.fore                 - цвет контура для маркера "закладка"
	caret.fore                            - цвет кретки
	caret.back                            - цвет строки на которой находится каретка
	selection.fore                        - цвет выделенного текста
	selection.back                        - цвет фона выделенного текста
	fore                                  - цвет текста по умолчанию (используется, когда для данного типа файла не определен лексер)
	back                                  - цвет фона по умолчанию (используется, когда для данного типа файла не определен лексер)
	calltipsbackgroundcolor               - цвет фона для диалога-подсказки (calltips) !не работает, не знаю почему, кидайте камни в разработчиков QScintill'ы
	calltipsforegroundcolor               - цвет текста для диалога-подсказки (calltips) !не работает, не знаю почему, кидайте камни в разработчиков QScintill'ы
	calltipshighlightcolor                - цвет выделенного элемента в диалоге-подсказке (calltips) !не работает, не знаю почему, кидайте камни в разработчиков QScintill'ы
	marginsbackgroundcolor                - цвет фона отступа слева
	marginsforegroundcolor                - цвет текста отступа слева
	foldmargincolors.back                 - цвет фона отступа с фолдингом (работает только в паре с foldmargincolors.fore, т.е. задавать надо и то и то)
	foldmargincolors.fore                 - второй цвет фона  отступа с фолдингом (работает только в паре с foldmargincolors.back, т.е. задавать надо и то и то)
	indentationguidesbackgroundcolor      - цвет линии между операторными скобками, если скобки не подсвечены
	indentationguidesforegroundcolor      - цвет линии между операторными скобками, если скобки подсвечены (каретка на одной из скобок)
	matchedbracebackgroundcolor           - цвет строки при подсветке скобок имеющих закрывающую/открывающую пару
	matchedbraceforegroundcolor           - цвет самой скобки при подсветке скобок имеющих закрывающую/открывающую пару
	unmatchedbracebackgroundcolor         - цвет строки при подсветке скобок не имеющих закрывающую/открывающую пару
	unmatchedbraceforegroundcolor         - цвет самой скобки при подсветке скобок не имеющих закрывающую/открывающую пару
	edgecolor                             - цвет индикатора длинны строки, используется, чтобы показать, что строка имеет длину превышющую максимальную
	indentationwidth                      - ширина отступа (в символах)
	tabwidth                              - ширина табуляции (в символах)
	edgecolumn                            - задать максимальную длину строки, используется, чтобы показать, что строка имеет длину превышющую максимальную (не ограничивает длину строки)
	autocompletionthreshold               - задать кол-во символов для автозавершения (не работает)
	caretwidth                            - ширина каретки в пиикселях
	caretlinevisible                      - подсвечивать строку на которой находится каретка
	setautostriptrailingspaces            - если true, то при сохранении, "лишние"(в конце строки) пробелы и табуляции будут удалены
	setwhitespacevisibility               - параметры отображения (или не отображения) пробелов и табуляций. 0 - не отображать, 1 - отображать, 2 - отображать после отступа
	font                                  - шрифт по умолчанию

	Помимо все, QyEditorBase при открытии файла, назначает редактору лексер, в соответствии с типом файла. Осуществляется это при помощи QyLexerFactory.

*/

class QyEditorBase : public QWidget {
	Q_OBJECT
	Q_PROPERTY( QString statusPattern READ statusPattern WRITE setStatusPattern )
	Q_PROPERTY( QString fileFilter READ fileFilter WRITE setFileFilter )
public:
	QyEditorBase( QWidget * parent=0 ); //!< Стандартный конструктор

	QString statusPattern() const { return p_statusPattern; } //!< статус строка-шаблон
	QString fileFilter() const { return p_fileFilter; } //!< фильтр файлов для диалогов
	QySciEdit * currentEdit() const; //!< текущий редактор

public slots:
	void openEmpty( const QString & section=QString() ); //!< открыть пустую закладку
	void openFile( QString fileName=QString(), const QString & section=QString() ); //!< открыть файл
	void openFileOnLine( QString fileName, int line, const QString & section=QString() ); //!< открыть файл на строке
	void saveCurrent(); //!< сохранить текущий файл
	void saveCurrentAs(); //!< сохранить текущий файл, с принудительным вызовом диалога, для выбора имени
	void closeCurrent(); //!< закрыть текущую закладку
	void closeCurrentSection(); //!< закрыть текущую группу файлов
	void clear(); //!< закрыть все группы файлов
	void updateStatus(); //!< обновить статус
	void setStatusPattern( const QString & statusPattern )
		{ p_statusPattern = statusPattern; updateStatus(); } //!< задать статус строку-шаблон
	void setFileFilter( const QString & fileFilter ) { p_fileFilter = fileFilter; } //!< задать филтер файлов для диалогов
	void loadSession( QString fileName=QString() ); //!< загрузить сессию
	void saveSession( QString fileName=QString() ); //!< сохранить сессию
	void switchCurrentFile(); //!< переключиться между файлами иеющими одинаковую основу
	void toggleBookmark(); //!< установить/удалить закладку на текущей строке
	void findNextBookmark(); //!< найти следующую закладку в текущем файле
	void updateConfig(); //!< обновить конфигурацию редакторов
	void findText( const QString & text=QString(), bool re=false, bool cs=false, bool wm=false ); //!< найти текст или вызвать диалог поиска
	void findTextNext(); //!< продолжить поиск текста
	void replaceText(); //!< вызвать диалог "поиск и замена"
	void toggleCurrentComment(); //!< закомментировать/раскомментировать текущую строку или выделенные строки
	void toggleCurrentFoldAll(); //!< свернуть/развернуть все блоки
	void toggleCurrentFoldCurrent(); //!< свернуть/развернуть текущий блок
	void gotoLine( int line=-1 ); //!< перейти на строку, если -1 то вызывает goto диалог. Считается что строки нумеруются с 1
	void reopenCurrentWithCodec( const QString & codecName=QString() ); //!< открыть текущий файл используя заданный кодек. Если кодек не задан, то пользователю будет выдан запрос со списком доступных кодеков. Файл при этом не перекодируется.

	static void setSciEditConfig( QMap<QString,QVariant> sciEditConfig ); //!< установить свойства для конфигурации редакторов

signals:
	void statusChanged( const QString & text ); //!< статус обновлен
	void fileSaved( const QString & fileName ); //!< файл сохранен

private slots:
	void currentTabChanged( const QString & section, QWidget * widget ); // выбрана новая закладка
	void editorModificationChanged( bool m );  // флаг модификации редактора изменил значение (например пользователь ввел символы или отменил все изменения)
	void encodingChanged( const QString & encodingName ); // кодировка файла была изменена (подразумевается что в текущем редакторе)
	void fileNameChanged( const QString & fileName ); // имя файла было изменено (подразумевается что в текущем редакторе)
	void linesChanged(); // кол-во строк было изменено (подразумевается что в текущем редакторе)
	void cursorPositionChanged( int line, int pos ); // текущее положение каретки было изменено (подразумевается что в текущем редакторе)
	void editMarginClicked( int margin, int line, Qt::KeyboardModifiers state ); // пользователь щелкнул на отступ (подразумевается что в текущем редакторе)
	void replaceOne( const QString & findText, const QString & replaceText, bool re, bool cs,
		bool wm ); // пользователь щелкнул "заменить один" в диалоге "поиск и замена"
	void replaceAll( const QString & findText, const QString & replaceText, bool re, bool cs,
		bool wm ); // пользователь щелкнул "заменить все" в диалоге "поиск и замена"
	void replaceInSelection( const QString & findText, const QString & replaceText, bool re,
		bool cs, bool wm ); // пользователь щелкнул "заменить в выделенном" в диалоге "поиск и замена"

private:
	QyTabPagePrivate * createPage( const QString & section, const QString & title,
		bool checkEmpty=true ); // создать страницу
	QyTabPagePrivate * currentPage() const; // текущая закладка
	QyTabPagePrivate * page( const QString & section, const QString & fileName ) const; // получить закладку по имени группы и имени файла
	QString validateSectionName( const QString & name ); // проверяет валидность имени группы закладок и возвращает "правильное имя"
	void checkEmpty(); // проверить, если вкладок нет вообще, то создать одну пустую
	QString fileDialogFilter(); // сформировать фильтр файлов для диалогов открыть/созранить, в соответствии с текущим списком лексеров
	QString validateFileName( const QString & fileName, const QString & filter ); // проверить, если пользователь в диалоге "сохранить" не указал расширение, то дополнить, при этом если расширений несколько, то запросить его у пользователя. Вообще, системные диалоги кде4 сами этим занимаются, но вдруг мы работаем на системе типа мсвс, где убогие диалоги ничего не проверяют.
	void applySciEditSettings( QySciEdit * edit ); // применить текущие настройки для заданного редактора

	QString p_statusPattern, // статус строка-шаблон
		p_fileFilter; // фильтр файлов для диалогов
	QyTabWidget * tabWidget; // таб виджет
	QyFindDialog * findDialog; // диалог поиска
	QyReplaceDialog * replaceDialog; // диалог "поиск и замена"
	QyGotoDialog * gotoDialog; // диалог "перейти на строку"
	QyCodecDialog * codecDialog; // диалог для смены кодировки
	QySciFindRequest lastRequest; //последний поисковый запрос
	struct QyFileInfoRecord {
		QString fileName;
		int pos;

		QyFileInfoRecord( const QString & fn, int p=-1 ) : fileName(fn), pos(p) {}

		bool operator == ( const QyFileInfoRecord & fir ) { return fir.fileName == fileName; }

	};
	QList<QyFileInfoRecord> p_switchFileStack;
	static QMap<QString,QVariant> p_sciEditConfig; // настройки редакторов

};

#endif/*__qyeditorbase__*/
